package ru.ifmo.rybakov.muclumon;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
 * Abstract base class for any element information of to be
 * stored in database. Each element may have an associated collection
 * of property values.
 * @author gleb.rybakov
 */
@Entity
@Table(name = "element")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.CHAR)
@DiscriminatorValue("E")
public abstract class Element {
	@Id
	@GeneratedValue
	private Integer id;
	
	@Column(name = "foreign_id")
	private Integer foreignId;
	
	private String name;
	
	@ManyToOne
	@JoinColumn(name = "revision_id")
	private Revision revision;
	
	@ManyToOne
	@JoinColumn(name = "reference")
	protected Element reference;
	
	@OneToMany(cascade = CascadeType.ALL, mappedBy = "element")
	private Collection<PropertyValue> propertyValues = new ArrayList<PropertyValue>();
	
	/**
	 * @return the id
	 */
	public Integer getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Integer id) {
		this.id = id;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the revision
	 */
	public Revision getRevision() {
		return revision;
	}

	/**
	 * @param revision the revision to set
	 */
	public void setRevision(Revision revision) {
		this.revision = revision;
	}

	public void addPropertyValue(PropertyValue value) {
		if (propertyValues == null) {
			propertyValues = new ArrayList<PropertyValue>();
		}
		if (propertyValues.add(value)) {
			value.setElement(this);
		}
	}

	public Boolean isPropertySet(Property p) {
		if (propertyValues == null) {
			return false;
		}
		Iterator<PropertyValue> it = propertyValues.iterator();
		while (it.hasNext()) {
			PropertyValue next = it.next();
			if (next.getProperty().equals(p)) {
				return true;
			}
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Element && id != null) {
			return id.equals(((Element) obj).id);
		}
		return super.equals(obj);
	}

	
	/**
	 * @return the foreignId
	 */
	public Integer getForeignId() {
		return foreignId;
	}

	
	/**
	 * @param foreignId the foreignId to set
	 */
	public void setForeignId(Integer foreignId) {
		this.foreignId = foreignId;
	}

	/**
	 * Returns the propertyValues
	 * @return The propertyValues
	 */
	public Collection<PropertyValue> getPropertyValues() {
		return propertyValues;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return name;
	}

	public Set<Property> getPropertySet() {
		Set<Property> properties = new HashSet<Property>();
		for (PropertyValue pv : propertyValues) {
			properties.add(pv.getProperty());
		}
		return properties;
	}

	public PropertyValue getSingleValue(Property property) {
		for (PropertyValue pv : propertyValues) {
			if (pv.getProperty().equals(property)) {
				return pv;
			}
		}
		return null;
	}
}
